package gestionedt.controllers;

import gestionedt.controllers.exceptions.NonexistentEntityException;
import gestionedt.controllers.exceptions.PreexistingEntityException;
import gestionedt.models.Creneau;
import gestionedt.models.Enseignant;
import gestionedt.models.Groupe;
import gestionedt.models.Prestation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.Query;
import javax.persistence.EntityNotFoundException;
import org.eclipse.persistence.config.HintValues;
import org.eclipse.persistence.config.QueryHints;

/**
 *
 *
 */
public class CreneauJpaController {

    public CreneauJpaController() {
        emf = Persistence.createEntityManagerFactory("GestionEDTPU");
    }
    private EntityManagerFactory emf = null;

    public EntityManager getEntityManager() {
        return emf.createEntityManager();
    }

    public void create(Creneau creneau) throws PreexistingEntityException, Exception {
        EntityManager em = null;
        try {
            em = getEntityManager();
            em.getTransaction().begin();
            em.persist(creneau);
            em.getTransaction().commit();
        } catch (Exception ex) {
            if (findCreneau(creneau.getNumero()) != null) {
                throw new PreexistingEntityException("Creneau " + creneau + " already exists.", ex);
            }
            throw ex;
        } finally {
            if (em != null) {
                em.close();
            }
        }
    }

    public void edit(Creneau creneau) throws NonexistentEntityException, Exception {
        EntityManager em = null;
        try {
            em = getEntityManager();
            em.getTransaction().begin();
            creneau = em.merge(creneau);
            em.getTransaction().commit();
        } catch (Exception ex) {
            String msg = ex.getLocalizedMessage();
            if (msg == null || msg.length() == 0) {
                Integer id = creneau.getNumero();
                if (findCreneau(id) == null) {
                    throw new NonexistentEntityException("The creneau with id " + id + " no longer exists.");
                }
            }
            throw ex;
        } finally {
            if (em != null) {
                em.close();
            }
        }
    }

    public void destroy(Integer id) throws NonexistentEntityException {
        EntityManager em = null;
        try {
            em = getEntityManager();
            em.getTransaction().begin();
            Creneau creneau;
            try {
                creneau = em.getReference(Creneau.class, id);
                creneau.getNumero();
            } catch (EntityNotFoundException enfe) {
                throw new NonexistentEntityException("The creneau with id " + id + " no longer exists.", enfe);
            }
            em.remove(creneau);
            em.getTransaction().commit();
        } finally {
            if (em != null) {
                em.close();
            }
        }
    }

    public List<Creneau> findCreneauEntities() {
        return findCreneauEntities(true, -1, -1);
    }

    public List<Creneau> findCreneauEntities(int maxResults, int firstResult) {
        return findCreneauEntities(false, maxResults, firstResult);
    }

    private List<Creneau> findCreneauEntities(boolean all, int maxResults, int firstResult) {
        EntityManager em = getEntityManager();
        try {
            Query q = em.createQuery("select object(o) from Creneau as o");
            if (!all) {
                q.setMaxResults(maxResults);
                q.setFirstResult(firstResult);
            }
            return q.getResultList();
        } finally {
            em.close();
        }
    }

    public Creneau findCreneau(Integer id) {
        EntityManager em = getEntityManager();
        try {
            return em.find(Creneau.class, id);
        } finally {
            em.close();
        }
    }

    public int getCreneauCount() {
        EntityManager em = getEntityManager();
        try {
            return ((Long) em.createQuery("select count(o) from Creneau as o").getSingleResult()).intValue();
        } finally {
            em.close();
        }
    }

    public List<Creneau> findCreneauxByNums(int[] numeros) {

        if(numeros.length == 0)
            return new ArrayList<Creneau>(0);

        EntityManager em = getEntityManager();
        try {
            String strQ = "SELECT c " +
                          "FROM Creneau AS c " +
                          "WHERE c.numero IN (?1";
            for(int i = 2; i <= numeros.length; i++)
                strQ += (", ?"+i);
            strQ += ")";
            Query q = em.createQuery(strQ);
            for(int i = 1; i <= numeros.length; i++)
                q.setParameter(i, numeros[i-1]);

            return q.getResultList();
        } finally {
            em.close();
        }
    }

    /**
     * Compte le nombre de prestations assurées ou suivies par l'enseignant et
     * la liste de groupes (passés en paramètres) lors du créneau dont le
     * numéro est aussi fourni en paramètre.
     * @param creneau créneau pour lequel on veut connaître le nombre de prestations
     * @param ens enseignant pour lequel les prestations qu'il assure seront comptées
     * @param groupes groupes pour lesquels les prestations qu'ils suivent seront comptées
     * @return nombre de prestations dans le créneau relatives à l'enseignant et aux groupes
     */
    public int countPrestationsPour(Creneau creneau, Enseignant ens, List<Groupe> groupes) {
        EntityManager em = getEntityManager();
        try {
            String strQ = "SELECT COUNT(DISTINCT p) " +
                          "FROM Prestation AS p " +
                            "LEFT JOIN p.groupeCollection g " +
                          "WHERE p.numCreneau = :creneau " +
                               "AND (p.idEnseignant = :ens ";
            if(groupes.size() > 0) {
               strQ += "OR g.id IN (?1";
               for(int i = 2; i <= groupes.size(); i++)
                    strQ += (", ?"+i);
                strQ += ")";
            }
            strQ += ")";
            
            Query q = em.createQuery(strQ);
            q.setParameter("creneau", creneau);
            q.setParameter("ens", ens);
            for(int i = 1; i <= groupes.size(); i++)
                q.setParameter(i, groupes.get(i-1).getId());

            Long nbP = (Long) q.getSingleResult();
            return nbP.intValue();
        } finally {
            em.close();
        }
    }

    /**
     * Récupère une liste de créneaux avec leurs relations préchargées.
     * @param numCreneau paramètre facultatif, s'il vaut null tous les creneaux seront récupérés
     * @return
     */
    private List<Creneau> findAvecRel(Integer numCreneau) {
        EntityManager em = getEntityManager();
        try {
            String strQ = "SELECT DISTINCT c " +
                          "FROM Creneau AS c " +
                            "LEFT JOIN FETCH c.prestationCollection " +
                            "LEFT JOIN FETCH c.enseignantCollection " +
                            "LEFT JOIN FETCH c.groupeCollection ";
            if(numCreneau != null)
                strQ += "WHERE c.numero = :numCreneau ";

            Query q = em.createQuery(strQ);
            q.setHint(QueryHints.REFRESH, HintValues.TRUE);
            if(numCreneau != null)
                q.setParameter("numCreneau", numCreneau);

            return q.getResultList();
        } finally {
            em.close();
        }
    }

    /**
     * Récupère une liste de créneaux avec leurs relations préchargées.
     * @return
     */
    public List<Creneau> findCreneauxAvecRel() {
        return this.findAvecRel(null);
    }

    /**
     * Recupère un créneau avec ses relations préchargées.
     * @param numCreneau
     * @return
     */
    public Creneau findCreneauAvecRel(Integer numCreneau) {
        List<Creneau> liste = findAvecRel(numCreneau);
        return (liste.size() > 0 ? liste.get(0) : null);
    }

}
